use core::ptr::NonNull;

use crate::*;

unsafe impl<'a, T, const L: usize> Facet<'a> for [T; L]
where
    T: Facet<'a>,
{
    const SHAPE: &'static Shape = &const {
        Shape {
            id: Shape::id_of::<Self>(),
            layout: Shape::layout_of::<Self>(),
            vtable: ValueVTable {
                type_name: |f, opts| {
                    if let Some(opts) = opts.for_children() {
                        write!(f, "[")?;
                        (T::SHAPE.vtable.type_name())(f, opts)?;
                        write!(f, "; {L}]")
                    } else {
                        write!(f, "[…; {L}]")
                    }
                },
                drop_in_place: ValueVTable::drop_in_place_for::<Self>(),
                default_in_place: {
                    if L == 0 {
                        // Zero-length arrays implement `Default` irrespective of the element type
                        Some(|target| unsafe { target.assume_init() })
                    } else if L <= 32 && T::SHAPE.vtable.has_default_in_place() {
                        Some(|target| unsafe {
                            let t_dip = T::SHAPE.vtable.default_in_place.unwrap();
                            let stride = T::SHAPE
                                .layout
                                .sized_layout()
                                .unwrap()
                                .pad_to_align()
                                .size();
                            for idx in 0..L {
                                t_dip(target.field_uninit_at(idx * stride));
                            }
                            target.assume_init()
                        })
                    } else {
                        // arrays do not yet implement `Default` for > 32 elements due
                        // to specializing the `0` len case
                        None
                    }
                },
                clone_into: {
                    if T::SHAPE.vtable.has_clone_into() {
                        Some(|src, dst| unsafe {
                            let src = src.get::<Self>();
                            let t_cip = T::SHAPE.vtable.clone_into.unwrap();
                            let stride = T::SHAPE
                                .layout
                                .sized_layout()
                                .unwrap()
                                .pad_to_align()
                                .size();
                            for (idx, src) in src.iter().enumerate() {
                                (t_cip)(
                                    PtrConst::new(NonNull::from(src)),
                                    dst.field_uninit_at(idx * stride),
                                );
                            }
                            dst.assume_init()
                        })
                    } else {
                        None
                    }
                },
                ..ValueVTable::new(|_, _| Ok(()))
            },
            ty: Type::Sequence(SequenceType::Array(ArrayType { t: T::SHAPE, n: L })),
            def: Def::Array(ArrayDef::new(
                &const {
                    ArrayVTable::new(
                        |ptr| unsafe {
                            let array = ptr.get::<[T; L]>();
                            PtrConst::new(NonNull::from(array))
                        },
                        |ptr| unsafe {
                            let array = ptr.as_mut::<[T; L]>();
                            PtrMut::new(NonNull::from(array))
                        },
                    )
                },
                T::SHAPE,
                L,
            )),
            type_identifier: "&[_; _]",
            type_params: &[TypeParam {
                name: "T",
                shape: T::SHAPE,
            }],
            doc: &[],
            attributes: &[],
            type_tag: None,
            // Array uses Def::Array, not inner, for element access
            inner: None,
            proxy: None,
            // [T; N] is covariant in T, computed from the element type
            variance: Shape::computed_variance,
        }
    };
}
